#include <my_header.h>
// socketpair函数创建的socket主要用于在同一台机器上
// 父进程负责发送消息和文件描述符。
// 子进程负责接收信息，并利用接收到的文件描述符进行写入操作。
// 的进程间通信 本地socket
/* Usage: ./1_socketpair */
int main(void){                                  
//用于返回两个连接的socket描述符(等价与socket的fd), 
//父子进程可以通过这个文件描述符进行通信
//(任何发送到sv[0]的数据都可以从sv[1]读取，反之任何发送到sv[1]的数据都可以从sv[0]读取。)
    int sofd[2];
    int res_sopair = socketpair(AF_LOCAL, SOCK_STREAM, 0, sofd);
    ERROR_CHECK(res_sopair, -1, "socketpair");

    // 创建一个子进程
    pid_t pid = fork();
    if(pid == 0)
    {
        // 子进程
        // int file = open("./1_recv", O_RDONLY);
        // ERROR_CHECK(file, -1 ,"open");
        // "sofd[0]:收消息" 读

        // 接收到的详细信息
        struct msghdr msg;
        bzero(&msg, sizeof(msg));

        // 准备正文信息
        char str[60] = { 0 };
        struct iovec vec[1];
        vec[0].iov_base = str;  // 一个数据的指针
        vec[0].iov_len = sizeof(str);//数据的长度(字节)
    
        msg.msg_iov = vec;
        msg.msg_iovlen = 1; //上个参数iovec数组中的长度

        // 准备控制信息
        // void *msg_control 本质是一个cmghdr类型指针
        struct cmsghdr *cms = (struct cmsghdr *)malloc(CMSG_LEN(sizeof(int)));
        cms->cmsg_len = CMSG_LEN(sizeof(int));  // 指明这个数据结构的大小
        cms->cmsg_level = SOL_SOCKET;   // 协议类型
        cms->cmsg_type = SCM_RIGHTS;    //消息的类型
        
        msg.msg_control = cms;
        msg.msg_controllen = CMSG_LEN(sizeof(int));

        sleep(3);
        int ret_rmsg = recvmsg(sofd[0], &msg, 0);   // 接收消息
        ERROR_CHECK(ret_rmsg, -1, "recvmsg");

        // struct cmsghdr结构体的第四个参数
        void *p = CMSG_DATA(cms);   // 获取控制数据
        int *fd= (int *)p;  //  转换为文件描述符指针
        printf("son_process fd :%d\n", *fd); // 输出文件描述符
                                            
        write(*fd, "1234567", 7);   // 将数据写入父进程打开的文件

        // 关闭socket描述符
        close(sofd[0]);
        close(sofd[1]);

        close(*fd); // 关闭发送过来的文件描述符
    } else
    {
        // 父进程
        // sofd[1]:用于发送消息
        int file_fd = open("1_recv", O_RDWR);   // 打开文件
        printf("main_process fd:%d \n", file_fd);   // 输出文件描述符

        struct msghdr msg;  // 消息头结构体
        bzero(&msg, sizeof(msg));   // 清空消息头

        // 准备正文信息
        char *str = "hello";    // 要发送的字符串   字符串本身无意义 只是唤醒子进程切换状态
        struct iovec vec[1];    // iovec结构数组
        vec[0].iov_base = str;  // 指向字符串
        vec[0].iov_len = strlen(str);   // 字符串长度

        msg.msg_iov = vec;  // 指向iovec数组
        msg.msg_iovlen = 1;    // iovec的长度

        // 准备控制信息
        struct cmsghdr *cms = (struct cmsghdr *)malloc(CMSG_LEN(sizeof(int)));
        cms->cmsg_len = CMSG_LEN(sizeof(int));  // 结构体大小
        cms->cmsg_level = SOL_SOCKET;
        cms->cmsg_type = SCM_RIGHTS;    // 指定控制信息类型

        void *p = CMSG_DATA(cms);   // 获取控制数据
        int *fd = (int *)p; // 转换为文件描述符指针
        *fd = file_fd;  // 将文件描述符赋值给控制信息

        msg.msg_control = cms;  // 将控制信息关联给msg
        msg.msg_controllen = CMSG_LEN(sizeof(int));

        sendmsg(sofd[1], &msg, 0);  // 发送消息

        close(file_fd); // 关闭文件描述符
        sleep(10);  // 暂停10秒以等待子进程处理

        close(sofd[0]);  // 关闭socket描述符
        close(sofd[1]);  // 关闭socket描述符
    }


    return 0;
}


